﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using LevelEditor.Document;
using LevelEditor.View;
using System.Drawing;
using System.Xml.Serialization;
using System.Xml;
using WeifenLuo.WinFormsUI.Docking;
using System.IO;
using PSTaskDialog;

namespace LevelEditor.Forms
{
    public partial class MainForm : Form
    {
        #region Fields

        string fileName = "Untitled.xml";

        #endregion

        #region Properties

        /// <summary>
        /// Position of the scrollbars on the panel that displays the level
        /// </summary>
        Point ScrollPosition { get; set; }

        /// <summary>
        /// True = file was changed
        /// </summary>
        public bool DirtyFile { get; set; }

        /// <summary>
        /// Level being edited
        /// </summary>
        public Level Level { get; set; }

        /// <summary>
        /// Current layer of level being edited
        /// </summary>
        public int CurrentLayer { get; set; }

        /// <summary>
        /// Window for displaying tiles
        /// </summary>
        public BasicForm TilesForm { get; set; }

        /// <summary>
        /// Palette containing available tiles for editing
        /// </summary>
        public PaletteForm Palette { get; set; }

        /// <summary>
        /// Form listing all layers in the level
        /// </summary>
        public LayerForm LayerForm { get; set; }

        /// <summary>
        /// Control used to draw all visible layers and grid
        /// </summary>
        public LayerViewControl LayerViewControl { get; set; }

        /// <summary>
        /// Manager of all changes made to this level
        /// </summary>
        public HistoryManager HistoryManager { get; set; }

        /// <summary>
        /// Form listing all undo-able changes to this level
        /// </summary>
        public HistoryForm HistoryForm { get; set; }

        #endregion

        #region Constructors

        public MainForm()
        {
            InitializeComponent();
            layerVisibilityToolStripButton.Checked = false;
        }

        #endregion

        #region Methods

        private void RefreshVisible()
        {
            layerVisibilityToolStripButton.Checked = !Level.Layers[CurrentLayer].Visible;
            if (layerVisibilityToolStripButton.Checked)
                layerVisibilityToolStripButton.Image = null;
            else
                layerVisibilityToolStripButton.Image = Properties.Resources.VisibleSmall;
            LayerViewControl.Invalidate();
        }

        /// <summary>
        /// Refreshes the window title to reflect level changes
        /// </summary>
        public void ResetTitle()
        {
            Text = "Derek's Awesome Tile Editor [" + fileName;
            if (DirtyFile)
                Text += "*";
            Text += "] - " + Level.Name;

        }

        /// <summary>
        /// Refreshes display information on important controls
        /// </summary>
        public void RefreshControlData()
        {
            currentLayerTSL.Text = "Current Layer: " + CurrentLayer;
            LayerViewControl.Reset();
            Palette.ResetToCurrentLayer(this);
            RefreshVisible();
            LayerForm.Reload();
        }

        /// <summary>
        /// Clears all level information and resets to default values
        /// </summary>
        public void Clear()
        {
            DirtyFile = false;
            CurrentLayer = 0;
            Level = new Level();
            Level.Layers.Add(new Layer());
            Level.Layers[0].Tiles = new List<uint>();
            for (int i = 0; i < Level.Layers[0].Size.Width * Level.Layers[0].Size.Height; ++i)
                Level.Layers[0].Tiles.Add(0);
            Level.Layers[0].TileSet = new TileSet();
            Level.Layers[0].TileSet.LoadDefaults();
            if(LayerViewControl != null)
                RefreshControlData();
        }

        /// <summary>
        /// Move layer at given index up one level.
        /// </summary>
        /// <param name="layerIndex">given index of layer</param>
        /// <returns>Layer's new index.</returns>
        public int MoveLayerUp(int layerIndex)
        {
            int newIndex = layerIndex;
            if (layerIndex + 1 < Level.Layers.Count)
            {
                newIndex = layerIndex + 1;
                HistoryManager.AddAndDo(new DoUndoMoveLayerUp(this, layerIndex));
                LayerForm.Reload();
            }
            return newIndex;
        }

        /// <summary>
        /// Move layer at given index down one level.
        /// </summary>
        /// <param name="layerIndex">given index of layer</param>
        /// <returns>Layer's new index.</returns>
        public int MoveLayerDown(int layerIndex)
        {
            int newIndex = layerIndex;
            if (layerIndex - 1 >= 0)
            {
                newIndex = layerIndex - 1;
                HistoryManager.AddAndDo(new DoUndoMoveLayerDown(this, layerIndex));
                LayerForm.Reload();
            }
            return newIndex;
        }

        public void ResizeLayer(int layerIndex, Size newSize)
        {
            if (layerIndex >= 0 && layerIndex < Level.Layers.Count)
            {
                HistoryManager.AddAndDo(new DoUndoLayerResize(this, layerIndex, newSize));
            }
        }

        #endregion

        #region Events

        private void MainForm_Load(object sender, EventArgs e)
        {
            Clear();
            TilesForm = new BasicForm();
            TilesForm.AutoScroll = true;
            TilesForm.Text = "Tiles";
            Palette = new PaletteForm();
            Palette.Options += new PaletteForm.OptionsEventHandler(Palette_Options);
            Palette.TileDefinitionChange += new PaletteForm.TileDefinitionChangeHandler(Palette_TileDefinitionChange);
            LayerForm = new LayerForm(this);
            LayerViewControl = new LayerViewControl(this);
            LayerViewControl.MouseDown += new MouseEventHandler(LayerViewControl_MouseDown);
            LayerViewControl.MouseMove += new MouseEventHandler(LayerViewControl_MouseMove);
            HistoryManager = new HistoryManager(this);
            HistoryManager.Changed += new HistoryManager.ChangedEventHandler(HistoryManager_Changed);
            HistoryManager.AddAction += new HistoryManager.AddActionEventHandler(HistoryManager_AddAction);
            HistoryForm = new HistoryForm(HistoryManager);
            HistoryForm.ClearMenuItemClicked += new HistoryForm.ClearMenuItemClickedEventHandler(clearHistoryToolStripMenuItem_Click);
            HistoryForm.UndoMenuItemClicked += new HistoryForm.UndoMenuItemClickedEventHandler(undoToolStripMenuItem_Click);
            HistoryForm.RedoMenuItemClicked += new HistoryForm.RedoMenuItemClickedEventHandler(redoToolStripMenuItem_Click);
            HistoryForm.SelectedIndexChanged += new HistoryForm.SelectedIndexChangedEventHandler(HistoryForm_SelectedIndexChanged);
            TilesForm.Controls.Add(LayerViewControl);
            TilesForm.Show(dockPanel, DockState.Document);
            Palette.Show(dockPanel, DockState.DockRight);
            HistoryForm.Show(Palette.Pane, DockAlignment.Bottom, .5);
            LayerForm.Show(HistoryForm.Pane, HistoryForm);
            pencilTSB_Click(this, new EventArgs());
            RefreshControlData();
        }

        void Palette_TileDefinitionChange(object sender, EventArgs e)
        {
            TileDefinitionsDialog tdd = new TileDefinitionsDialog(Level.Layers[CurrentLayer].TileSet);
            DialogResult dr = tdd.ShowDialog(this);
            if (dr == DialogResult.OK)
            {
                HistoryManager.AddAndDo(new DoUndoTileSetOptions(this, tdd.TileSet));
            }
            tdd.Dispose();
        }

        void Palette_Options(object sender, EventArgs e)
        {
            TileSetDialog tsd = new TileSetDialog(Level.Layers[CurrentLayer].TileSet);
            DialogResult dr = tsd.ShowDialog(this);
            if (dr == DialogResult.OK)
            {
                HistoryManager.AddAndDo(new DoUndoTileSetOptions(this, tsd.TileSet));
            }
            tsd.Dispose();
        }

        void LayerViewControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (LayerViewControl.SelectionTiles.Size != Size.Empty && e.Button == MouseButtons.Left)
            {
                if (LayerViewControl.Tool == LayerViewControl.PaintTool.PENCIL)
                {
                    HistoryManager.AddAndDo(new DoUndoPencilTile(this, LayerViewControl.SelectionTiles, 
                        Palette.pc.SelectedTiles.Location));
                }
                else if (LayerViewControl.Tool == LayerViewControl.PaintTool.BUCKET)
                {
                    HistoryManager.AddAndDo(new DoUndoBucketTile(this, LayerViewControl.SelectionTiles.Location,
                        Palette.pc.SelectedTiles));
                }
            }
            else if (e.Button == MouseButtons.Left)
            {
                MessageBox.Show(this, "You must select a tile from the pallette window first.", "No Tiles Selected!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
        void LayerViewControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (LayerViewControl.SelectionTiles.Size != Size.Empty && e.Button == MouseButtons.Left)
            {
                if (LayerViewControl.Tool == LayerViewControl.PaintTool.PENCIL)
                {
                    HistoryManager.AddAndDo(new DoUndoPencilTile(this, LayerViewControl.SelectionTiles,
                        Palette.pc.SelectedTiles.Location));
                    LayerViewControl.Invalidate();
                }
            }
            else if (e.Button == MouseButtons.Left)
            {
                MessageBox.Show(this, "You must select a tile from the pallette window first.", "No Tiles Selected!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }

        }


        void HistoryManager_AddAction(object sender, EventArgs e)
        {
            DirtyFile = true;
            ResetTitle();
        }

        void HistoryManager_Changed(object sender, EventArgs e)
        {
            undoToolStripMenuItem.Enabled = true;
            redoToolStripMenuItem.Enabled = true;
            clearHistoryToolStripMenuItem.Enabled = true;
            if (HistoryManager.TimeLine.Count == 1)
            {
                undoToolStripMenuItem.Enabled = false;
                redoToolStripMenuItem.Enabled = false;
                clearHistoryToolStripMenuItem.Enabled = false;
            }
            else
            {
                if (HistoryManager.CurrentActionIndex == 0)
                {
                    undoToolStripMenuItem.Enabled = false;
                }
                if (HistoryManager.CurrentActionIndex == HistoryManager.TimeLine.Count - 1)
                {
                    redoToolStripMenuItem.Enabled = false;
                }
            }
        }

        void HistoryForm_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = HistoryManager.TimeLine.Count - HistoryForm.SelectedIndex - 1;
            if (index >= 0 && HistoryManager.TimeLine.Count > 0)
                HistoryManager.SetCurrentActionTo(index);
        }

        //void HistoryForm_RedoMenuItemClicked(object sender, EventArgs e)
        //{
        //    HistoryManager.Redo();
        //}

        //void HistoryForm_UndoMenuItemClicked(object sender, EventArgs e)
        //{
        //    HistoryManager.Undo();
        //}

        //void HistoryForm_ClearMenuItemClicked(object sender, EventArgs e)
        //{
        //    HistoryManager.Clear();
        //}

        private void paletteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Palette.Show();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutThis at = new AboutThis();
            at.ShowDialog(this);
            at.Dispose();
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (DirtyFile)
            {
                //cTaskDialog.ForceEmulationMode = true;
                DialogResult result = cTaskDialog.ShowSaveDontSaveDialogBox(this, "Level Has Changed", fileName);
                //DialogResult result = MessageBox.Show(
                //    "This level has been changed. Do you wish to save the changes?",
                //    "Level Changed!", MessageBoxButtons.YesNoCancel);
                if (result == DialogResult.Yes)
                {
                    if (TryToSave(true) == DialogResult.Cancel)
                        e.Cancel = true;
                }
                else if (result == DialogResult.Cancel)
                {
                    e.Cancel = true;
                }
            }
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DirtyFile)
            {
                DialogResult result = MessageBox.Show(
                    "This level has been changed. Do you wish to save the changes?", 
                    "Level Changed!", MessageBoxButtons.YesNoCancel);
                if (result == DialogResult.Yes)
                {
                    if (fileName == "Untitled.xml")
                        saveAsToolStripMenuItem_Click(this, new EventArgs());
                    else
                        saveToolStripMenuItem_Click(this, new EventArgs());
                }
                else if (result == DialogResult.Cancel)
                    return;
            }
            Clear();
            LayerViewControl.Clear();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (DirtyFile)
            {
                DialogResult result = MessageBox.Show(
                    "This level has been changed. Do you wish to save the changes?",
                    "Level Changed!", MessageBoxButtons.YesNoCancel);
                if (result == DialogResult.Yes)
                {
                    if (fileName == "Untitled.xml")
                        saveAsToolStripMenuItem_Click(this, new EventArgs());
                    else
                        saveToolStripMenuItem_Click(this, new EventArgs());
                }
                else if (result == DialogResult.Cancel)
                    return;
            }
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.DefaultExt = "xml";
            ofd.Filter = "XML Level File (*.xml)|*.xml|All Files (*.*)|*.*";
            DialogResult dr = ofd.ShowDialog(this);
            if (dr == DialogResult.OK)
            {
                try
                {
                    XmlReader reader = XmlReader.Create(ofd.FileName);
                    XmlSerializer serializer = new XmlSerializer(typeof(Level));
                    CurrentLayer = 0;
                    Level = (serializer.Deserialize(reader) as Level);
                    reader.Close();
                    HistoryManager.Clear();
                    DirtyFile = false;
                    fileName = ofd.FileName;
                    ResetTitle();
                    LayerViewControl.Clear();
                    RefreshControlData();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(this, ex.Message, "Invalid File", MessageBoxButtons.OK);
                }
            }
            ofd.Dispose();
        }

        private DialogResult TryToSave(bool force)
        {
            DialogResult result = DialogResult.OK;
            if (fileName == "Untitled.xml" || force)
            {
                SaveFileDialog sfd = new SaveFileDialog();
                sfd.DefaultExt = "xml";
                sfd.Filter = "XML Level File (*.xml)|*.xml|All Files (*.*)|*.*";
                result = sfd.ShowDialog(this);
                if (result == DialogResult.OK)
                {
                    fileName = sfd.FileName;
                }
                else
                {
                    sfd.Dispose();
                    return result;
                }
                sfd.Dispose();
            }
            try
            {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.CloseOutput = true;
                settings.Indent = true;
                XmlWriter writer = XmlWriter.Create(fileName, settings);
                XmlSerializer serializer = new XmlSerializer(typeof(Level));
                serializer.Serialize(writer, Level);
                writer.Close();
                DirtyFile = false;
                ResetTitle();
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.Message, "Invalid File", MessageBoxButtons.OK);
            }
            return result;
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TryToSave(false);
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TryToSave(true);
        }

        private void pencilTSB_Click(object sender, EventArgs e)
        {
            pencilToolStripButton.Checked = true;
            bucketToolStripButton.Checked = false;
            tilePickerToolStripButton.Checked = false;
            LayerViewControl.Tool = LayerViewControl.PaintTool.PENCIL;
            TilesForm.Cursor = CustomCursors.Pencil;
        }

        private void bucketTSB_Click(object sender, EventArgs e)
        {
            bucketToolStripButton.Checked = true;
            pencilToolStripButton.Checked = false;
            tilePickerToolStripButton.Checked = false;
            LayerViewControl.Tool = LayerViewControl.PaintTool.BUCKET;
            TilesForm.Cursor = CustomCursors.Bucket;
        }

        private void tilePickerTSB_Click(object sender, EventArgs e)
        {
            tilePickerToolStripButton.Checked = true;
            pencilToolStripButton.Checked = false;
            bucketToolStripButton.Checked = false;
            LayerViewControl.Tool = LayerViewControl.PaintTool.TILEPICKER;
            TilesForm.Cursor = CustomCursors.Picker;
        }

        private void upLayerTSB_Click(object sender, EventArgs e)
        {
            if (CurrentLayer < Level.Layers.Count - 1)
            {
                ++CurrentLayer;
                RefreshControlData();
            }
        }

        private void downLayerTSB_Click(object sender, EventArgs e)
        {
            if (CurrentLayer > 0)
            {
                --CurrentLayer;
                RefreshControlData();
            }
        }

        private void toggleLayerVisibilityTSB_Click(object sender, EventArgs e)
        {
            Level.Layers[CurrentLayer].Visible = !Level.Layers[CurrentLayer].Visible;
            RefreshControlData();
        }

        public void addLayerTSB_Click(object sender, EventArgs e)
        {
            LayerDialog ld = new LayerDialog(null);
            DialogResult dr = ld.ShowDialog(this);
            if (dr == DialogResult.OK)
            {
                HistoryManager.AddAndDo(new DoUndoAddLayer(this, ld.Layer));
            }
            ld.Dispose();
        }

        public void removeLayerTSB_Click(object sender, EventArgs e)
        {
            if (Level.Layers.Count > 1)
            {
                HistoryManager.AddAndDo(new DoUndoRemoveLayer(this, CurrentLayer));
            }
            else
                MessageBox.Show(this, "Level must contain at least one layer.",
                    "Can not remove last layer.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }

        public void layerOptionsTSB_Click(object sender, EventArgs e)
        {
            LayerDialog ld = new LayerDialog(Level.Layers[CurrentLayer]);
            DialogResult dr = ld.ShowDialog(this);
            if (dr == DialogResult.OK)
            {
                RefreshControlData();
            }
            ld.Dispose();
        }

        private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            EditorOptionsDialog eod = new EditorOptionsDialog();
            eod.LayerBackColor = LayerViewControl.BackColor;
            eod.LayerEnableGrid = LayerViewControl.GridVisible;
            eod.LayerGridColor = LayerViewControl.ForeColor;
            eod.LayerGridThickness = LayerViewControl.GridThickness;
            eod.PaletteBackColor = Palette.BackColor;
            eod.PaletteGridColor = Palette.ForeColor;
            eod.PaletteEnableGrid = Palette.GetGridVisible();
            eod.PaletteGridThickness = Palette.GetGridThickness();
            DialogResult dr = eod.ShowDialog(this);
            if (dr == DialogResult.OK)
            {
                LayerViewControl.BackColor = eod.LayerBackColor;
                LayerViewControl.ForeColor = eod.LayerGridColor;
                LayerViewControl.GridVisible = eod.LayerEnableGrid;
                LayerViewControl.GridThickness = eod.LayerGridThickness;
                Palette.SetPaletteBackColor(eod.PaletteBackColor);
                Palette.SetPaletteGridColor(eod.PaletteGridColor);
                Palette.SetGridThickness(eod.PaletteGridThickness);
                Palette.SetGridVisible(eod.PaletteEnableGrid);
                LayerViewControl.Invalidate();
                Palette.Invalidate();
            }
            eod.Dispose();
        }

        private void layerOptionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            layerOptionsTSB_Click(sender, e);
        }

        private void layersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            LayerForm.Show();
        }

        private void showLayerWindowToolStripButton_Click(object sender, EventArgs e)
        {
            LayerForm.Show();
        }

        private void levelOptionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // TODO: implement level options dialog with custom data list
        }

        private void historyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            HistoryForm.Show();
        }

        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            HistoryManager.Undo();
        }

        private void redoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            HistoryManager.Redo();
        }

        private void clearHistoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show(this, "This action can not be undone. Are you sure you wish to continue?", "Clear History Confirmation", MessageBoxButtons.YesNo);
            if (dr == DialogResult.Yes)
            {
                HistoryManager.Clear();
            }
        }

        private void MainForm_Deactivate(object sender, EventArgs e)
        {
            ScrollPosition = dockPanel.AutoScrollPosition;
        }

        private void MainForm_Activated(object sender, EventArgs e)
        {
            dockPanel.AutoScrollPosition = new Point(Math.Abs(ScrollPosition.X), Math.Abs(ScrollPosition.Y));
        }

        private void tilesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TilesForm.Show();
        }

        #endregion
    }
}
